﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace Mrkz.ImageManipulation.FrequencyFiltering
{
    public class ILFFTEngine
    {
        static ILNumerics.ILRetArray<ILNumerics.complex> NormalizedFFT1D(ILNumerics.ILInArray<ILNumerics.complex> InputArray, int Direction)
        {
            if (Direction == 1)
                return ILNumerics.ILMath.fft(InputArray);
            else
                return ILNumerics.ILMath.ifft(InputArray);

        }

        static int RCtoI(int r, int c,int height)
        {
            return r + c * height;
        }

        static TImage<ComplexPixel> ParallelFFT2D(TImage<ComplexPixel> InputData, int Direction)
        {
            int Height = (InputData.Height);
            int Width = (InputData.Width);
            
            ILNumerics.ILArray<ILNumerics.complex> _R = ILNumerics.ILMath.zeros<ILNumerics.complex>(Height, Width);
            ILNumerics.ILArray<ILNumerics.complex> _G = ILNumerics.ILMath.zeros<ILNumerics.complex>(Height, Width);
            ILNumerics.ILArray<ILNumerics.complex> _B = ILNumerics.ILMath.zeros<ILNumerics.complex>(Height, Width);         

            System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
            s.Start();

            ILNumerics.complex[] _Rw = (_R.GetArrayForWrite());
            ILNumerics.complex[] _Gw = (_G.GetArrayForWrite());
            ILNumerics.complex[] _Bw = (_B.GetArrayForWrite());

            //per ogni colonna            
            ILNumerics.complex cnum=new ILNumerics.complex();
            for (int c=0;c<Width;c++)
            {
                for (int r = 0; r < Height; r++)
                {
                    int i=RCtoI(r,c,Height);

                    cnum.real = InputData._data[r, c].CRed.Real;
                    cnum.imag = InputData._data[r, c].CRed.Imaginary;
                    _Rw[i] = cnum;
                   
                    cnum.real = InputData._data[r, c].CGreen.Real;
                    cnum.imag = InputData._data[r, c].CGreen.Imaginary;
                    _Gw[i] = cnum;

                    cnum.real = InputData._data[r, c].CBlue.Real;
                    cnum.imag = InputData._data[r, c].CBlue.Imaginary;
                    _Bw[i] = cnum;

                }
            }

            _Rw = null;
            _Gw = null;
            _Bw = null;
           
            TimeSpan t1 = s.Elapsed;
            System.Threading.Thread.MemoryBarrier();      

            ILNumerics.ILArray<ILNumerics.complex> out_R;
            ILNumerics.ILArray<ILNumerics.complex> out_G;
            ILNumerics.ILArray<ILNumerics.complex> out_B;
            
            if (Direction >= 0)
            {
                out_R= ILNumerics.ILMath.fft2(_R);
                out_G= ILNumerics.ILMath.fft2(_G);
                out_B= ILNumerics.ILMath.fft2(_B);
            }
            else
            {
                out_R = ILNumerics.ILMath.ifft2(_R);
                out_G = ILNumerics.ILMath.ifft2(_G);
                out_B = ILNumerics.ILMath.ifft2(_B);
            }

            TimeSpan t2 = s.Elapsed;

            Complex[,] c_R = new Complex[Height, Width];
            Complex[,] c_B = new Complex[Height, Width];
            Complex[,] c_G = new Complex[Height, Width];

            System.Threading.Thread.MemoryBarrier();

            _Rw = out_R.GetArrayForRead();
            _Gw = out_G.GetArrayForRead();
            _Bw = out_B.GetArrayForRead();

            for (int c = 0; c < Width; c++)
            {
                for (int r = 0; r < Height; r++)
                {
                    int i = RCtoI(r, c, Height);
                    c_R[r, c] = new Complex(_Rw[i].real, _Rw[i].imag);
                    c_G[r, c] = new Complex(_Gw[i].real, _Gw[i].imag);
                    c_B[r, c] = new Complex(_Bw[i].real, _Bw[i].imag);
                }
            }

            _Rw = null;
            _Gw = null;
            _Bw = null;

            TimeSpan t3 = s.Elapsed;
            Console.WriteLine("{0}ms {1}ms {2}ms", t1.TotalMilliseconds, t2.TotalMilliseconds, t3.TotalMilliseconds);

            return ImageFactory<ComplexPixel>.FromComplexPixelArray(Height, Width, InputData.CropArea, c_R, c_G, c_B);
        }

        public ILFFTEngine()
        {
            ILNumerics.ILMath.FFTImplementation = new ILNumerics.Native.ILFFTW3FFT();
        }

        public TImage<ComplexPixel> ForwardTransform(TImage<Pixel> Source)
        {            
            TImage<ComplexPixel> i = ImageFactory<Pixel>.FromImage(Source);

            TImage<ComplexPixel> r = ParallelFFT2D(i, 1);

            return r;
        }

        public TImage<Pixel> InverseTransform(TImage<ComplexPixel> Source)
        {
            TImage<ComplexPixel> r = null;

            r = ParallelFFT2D(Source, -1);

            return ImageConverter<Pixel,ComplexPixel>.FromImageWithCrop(r);
        }


    }
}
